#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define QSONT_LEN_HEAD 8
#define QSONT_MAX_LEN 65535
/* 清空一个byte缓冲区 */
void clear_buf(char *buf, int len) {
	for (int i=0; i<len; ++i) {
		*(buf++) = 0;
	}
}

/* 创建一个带qsont协议头的字符串，需要传入你要发送的消息体 */
static char *crt_head(char const *msg, size_t len) {
	char *buf = (char*)malloc(len*sizeof(char)); *buf = 0;
	if (!buf) {
		perror("malloc");
		return NULL;
	}
	if (snprintf(buf, QSONT_LEN_HEAD, "%zu", len-QSONT_LEN_HEAD) >= sizeof(buf)) {
		perror("snprintf");
		return NULL; // message is too long
	}
	strcpy(buf+QSONT_LEN_HEAD, msg);
	return buf;
}

/* 创建一个qsont协议头的字符串，需要传入要发送的要发送的消息体 */
static size_t crt_qs_head(char const *msg, char **np) {
	if (*np) return 0; // 需要传入空指针的地址
	size_t len = strlen(msg) + QSONT_LEN_HEAD + 1; // strlen()不计算\0结束符号
	if (len > QSONT_MAX_LEN) return 0;
	*np = (char *) malloc(len * sizeof(char)); **np = 0;
	if (!*np) {
		perror("malloc");
		return 0;
	}
	snprintf(*np, QSONT_LEN_HEAD, "%zu", len-QSONT_LEN_HEAD); // 写入协议头
	strcpy(*np + QSONT_LEN_HEAD, msg); // 写入消息体
	return len;
}

/* 发送qsont协议消息 */
int qs_send(int sock, char const * msg) {
	char *buf = NULL;
	size_t len = crt_qs_head(msg, &buf);
	if (!len || !buf) return 1;
	else {
		send(sock, buf, len, 0);
		free(buf);
		return 0;
	}
}
/* 收取qsont协议头，失败返回0
 * 
 * 特别注意，atoi()不进行错误检测，建议使用strtol() */
static int qs_recv_head(int sock) {
	char buf[QSONT_LEN_HEAD] = {0};
	read(sock, buf, QSONT_LEN_HEAD);
	return atoi(buf);
}

/* 收取qsont消息体，需要传入套接字句柄、消息体的长度
 *
 * 成功返回需要释放的字符串，失败返回NULL。 */
static char *qs_recv_body(int sock, int len) {
	char *buf = (char*)malloc((len)*sizeof(char)); *buf=0;
	if (!buf) {
		perror("malloc");
		return NULL;
	}
	else {
		read(sock, buf, len);
		return buf;
	}
}

/* 接收qsont消息，需要传入套接字句柄和最大字节限制，失败则返回NULL。
 *
 * 需要特别注意的是，它返回的字符串是需要使用free释放的
 */
char *qs_recv(int sock, int max) {
	int len = qs_recv_head(sock);
	if (len <= 0 || len > max)
		return NULL; // invalid head
	return qs_recv_body(sock, len);
}
#endif
